#include "SQLiteBlobIO.h"
#include "SQLiteDatabase.h"
#include "../../common/CppMemPool.h"
#include "../../common/ObjPtrT.h"
#include "sqlite3.h"

namespace cppflib
{

namespace database
{

namespace sqlite
{


PRIVATE sqlite3_blob * __GetBlobHandle(ptrany_t blobHandle)
{
   return static_cast<sqlite3_blob*>(blobHandle);
}

SQLiteBlobIO::SQLiteBlobIO(void)
{
   this->pDB = NULL;
   this->blobHandle = NULL;
}

SQLiteBlobIO::~SQLiteBlobIO(void)
{
   Close();
}

SQLiteBlobIO::SQLiteBlobIO(SQLiteDatabase * pDB)
{
   this->pDB = pDB;
   cppmempool::Retain(this->pDB);
   this->blobHandle = NULL;
}

/**
 *  Open a blob cell for reading/writing. 
 *  The cell is located by 1) table name, 2) column name, 3) row ID
 *
 *  @return false if open failed
 */
bool SQLiteBlobIO::Open(const CFString &dbName, const CFString &tabName, const CFString &blobColName, i64_t rowID, bool readOnly)
{
   if (blobHandle != NULL)
      return false;

   if (tabName.IsEmpty() || blobColName.IsEmpty() || rowID <= 0)
      return false;

   sqlite3_blob *handle = NULL;

   ObjPtrT<char> strDBName(dbName.ToUtf8String());
   ObjPtrT<char> strTabName(tabName.ToUtf8String());
   ObjPtrT<char> strColName(blobColName.ToUtf8String());
   int ret = sqlite3_blob_open(static_cast<sqlite3*>(pDB->GetDbConn()), 
                               strDBName, strTabName, strColName, 
                               rowID,
                               (readOnly ? 0 : 1), &handle);
   if (ret == SQLITE_OK) {
      this->blobHandle = static_cast<ptrany_t>(handle);
      return true;
   }

   return false;
}

/**
 *  Read blob data
 *
 *  @param [in, out] binBuf -- buffer to hold read in data
 *  @param [in] binBufOffset -- offset into the binBuf to start filling in data
 *  @param [in] blobOffset -- offset into the blob data to start reading
 *  @param [in] nrBytes -- number of bytes to read
 */
bool SQLiteBlobIO::Read(collections::BinBuf &binBuf, int binBufOffset, int blobOffset, int nrBytes)
{
    if (binBufOffset < 0 || blobOffset < 0 || nrBytes < 0)
       return false;

    binBuf.Realloc(binBufOffset + nrBytes);
    u8_t * pReadBuf = binBuf.GetRawBuf() + binBufOffset;

    int ret = sqlite3_blob_read(__GetBlobHandle(blobHandle), pReadBuf, nrBytes, blobOffset);
    if (ret == SQLITE_OK) {
       binBuf.SetValidLength(binBufOffset + nrBytes);
       return true;
    }

    return false;
}

/**
 *  Write blob data
 *
 *  @param [in] binBuf -- buffer containing data to be written
 *  @param [in] binBufOffset -- offset into the binBuf to start writing
 *  @param [in] blobOffset -- offset into the blob data to start writing
 *  @param [in] nrBytes -- number of bytes to write
 */
bool SQLiteBlobIO::Write(const collections::BinBuf &binBuf, int binBufOffset, int blobOffset, int nrBytes)
{
   if (binBufOffset < 0 || blobOffset < 0 || nrBytes < 0)
       return false;

   if (binBuf.GetLength() < static_cast<sz_t>(binBufOffset + nrBytes))
       return false;

   u8_t * pWriteBuf = binBuf.GetRawBuf() + binBufOffset;

   int ret = sqlite3_blob_write(__GetBlobHandle(blobHandle), pWriteBuf, nrBytes, blobOffset);
   return (ret == SQLITE_OK);
}

/**
 *  Move to read/write another cell in the same table and same column
 */
bool SQLiteBlobIO::Reopen(i64_t anotherRowID)
{
   if (blobHandle == NULL)
      return false;

   int ret = sqlite3_blob_reopen(__GetBlobHandle(blobHandle), anotherRowID);
   return (ret == SQLITE_OK);
}

/**
 *  Get the number of bytes of the opend blob cell
 */
int SQLiteBlobIO::GetBlobLength()
{
   if (blobHandle == NULL) 
      return -1;

   return sqlite3_blob_bytes(__GetBlobHandle(blobHandle));
}

/**
 *  Release resources
 */
void SQLiteBlobIO::Close()
{
   if (blobHandle) {
      sqlite3_blob_close(__GetBlobHandle(blobHandle));
      blobHandle = NULL;
   }

   if (pDB) {
      cppmempool::Release(pDB);
      pDB = NULL;
   }
}

    

} // end of namespace sqlite

} // end of namespace database

} // end of namespace cppflib

